package pilgun.Multithread.ConnectionPool;

import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.ArrayDeque;
import java.util.Map;
import java.util.Properties;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;


public class ConnectionPool {
	
	private final int connectionNumber =  5;
	private final Semaphore sem = new Semaphore(connectionNumber);
	private final int WAITING_TIMEOUT = 5000;
	private final int IDLE_TIMEOUT = 5000;
	private Queue<Connection> poolQueue = new ArrayDeque<Connection> (connectionNumber);
	
	
	class TimeoutChecker extends Thread{
		private Connection[] connectionArray;
		public TimeoutChecker(Connection[] connectionArray) {
			super();
			this.connectionArray = connectionArray;
		}

		@Override
		public void run(){
			for(;;){
				for (Connection connection:connectionArray){
					synchronized(ConnectionPool.this){
						if (connection.isCheckedOut() && (System.currentTimeMillis() - connection.getRemovalTime() > IDLE_TIMEOUT)){
							checkIn(connection);
						}
					}
				}
			}
		}
	}

	
	
	public ConnectionPool(String url, String user, String passwd) throws SQLException, ClassNotFoundException {
		super();
		 Connection[] connectionArray = new Connection[connectionNumber];
		for (int i=0;i<connectionNumber; i++){
			Connection connection = new Connection(url, user, passwd, this);
			poolQueue.add(connection);
			connectionArray[i] = connection;
		}
		TimeoutChecker checker = new TimeoutChecker(connectionArray);
		checker.setDaemon(true);
		checker.start();
	}
	
	public Connection checkOut() throws SQLException, InterruptedException, TooLongException{
	 	boolean gotIt = sem.tryAcquire(WAITING_TIMEOUT,TimeUnit.MILLISECONDS);
		if (gotIt){
			synchronized (this) {
				Connection c = poolQueue.poll();
				System.out.println("One removed "+ c.getNumber());
				c.setCheckedOut();
				c.setRemovalTime(System.currentTimeMillis());
				return c;
			}
		}else{
			throw new TooLongException();
		}
	}
	
	public void checkIn(Connection c){
		synchronized (this) {
			if (!poolQueue.contains(c)){
				poolQueue.offer(c);
				c.setCheckedIn();
				System.out.println("One put back "+ c.getNumber());
				sem.release();
			}
		}
	}
	
}
